#include "game_core.h"

GameCore::GameCore()
{

}

GameCore::~GameCore()
{
  this->cleanData();
}

void		GameCore::generate(GameParams *gameParams)
{
  this->cleanData();
  this->gameParams = gameParams;

  this->createSystems();
  this->createVortex();
  this->createPlanets();
}

void		GameCore::createVortex()
{
  int		i;
  int		begin;

  for(i = 0, begin = 0; i < (int)this->systems.size(); i++)
    {
      begin = this->vortexList.size();
      this->createVortexRings(i);
      if((int)this->vortexList.size() > begin && begin > 0)
	{
	  this->linkToClosest(this->vortexList.at(begin -1)->sysId1
			      , this->vortexList.at(begin -1)->sysId2
			      , this->vortexList.back()->sysId1
			      , this->vortexList.back()->sysId2);
	}
    }

  for(i = 0; i < (int)this->systems.size(); i++)
    if(this->nbOfLinksInSystem(i) == 0)
      this->chooseClosestSystem(i);
  for(i = 0; i < (int)this->systems.size(); i++)
    if(this->nbOfLinksInSystem(i) == 0)
      this->forceLink(i);
}

// force a star to link with its closest neighbor
void		GameCore::forceLink(int sys)
{
  int		i, tmpSys, tmpDist, dist;

  for(i = 0, tmpSys = -1, tmpDist = 1000000; i < (int)this->systems.size(); i++)
    if(i != sys)
      {
	dist = this->getDist(i, sys);
	if(dist < tmpDist)
	  {
	    tmpDist = dist;
	    tmpSys = i;
	  }
      }
  if(tmpSys != -1)
      this->addLink(tmpSys, sys);
}

// link a single system to the closest system
void		GameCore::chooseClosestSystem(int sys)
{
  int		i, tmpSys, tmpDist, dist;

  for(i = 0, tmpSys = -1, tmpDist = 1000000; i < (int)this->systems.size(); i++)
    if(i != sys && this->nbOfLinksInSystem(i) != 0)
      {
	dist = this->getDist(i, sys);
	if(dist < tmpDist)
	  {
	    tmpDist = dist;
	    tmpSys = i;
	  }
      }
  if(tmpSys != -1)
    {
      this->addLink(tmpSys, sys);
      this->createVortexRings(tmpSys);
    }
}

// link 2 links together
void		GameCore::linkToClosest(int sys11, int sys12, int sys21
					, int sys22)
{
  int		dist1, dist2, dist3, dist4;
  int		select1, select2, selDist;

  dist1 = this->getDist(sys11, sys21);
  dist2 = this->getDist(sys11, sys22);
  dist3 = this->getDist(sys12, sys21);
  dist4 = this->getDist(sys12, sys22);

  select1 = sys11;
  select2 = sys21;
  selDist = dist1;
  if(dist2 < selDist)
    {
      select1 = sys11;
      select2 = sys22;
      selDist = dist2;
    }
  if(dist3 < selDist)
    {
      select1 = sys12;
      select2 = sys21;
      selDist = dist3;
    }
  if(dist4 < selDist)
    {
      select1 = sys12;
      select2 = sys22;
      selDist = dist4;
    }
  this->addLink(select1, select2);
}

int		GameCore::getDist(int sys1, int sys2)
{
  return((int)sqrt(pow(systems.at(sys1)->getX() - systems.at(sys2)->getX(), 2)
	      + pow(systems.at(sys1)->getY() - systems.at(sys2)->getY(), 2)
	      + pow(systems.at(sys1)->getZ() - systems.at(sys2)->getZ(), 2)));
}

// find free stars close from each others and link them
void		GameCore::createVortexRings(int sys)
{
  int		i, dist;
  int		tmpSys, tmpDist;

  for(i = 0, tmpSys = 0, tmpDist = 10000; i < (int)this->systems.size(); i++)
    if(i != sys && this->nbOfLinksInSystem(i) == 0)
      {
	dist = this->getDist(i, sys);
	if(dist < 90 && dist < tmpDist)
	  {
	    tmpDist = dist;
	    tmpSys = i;
	  }
      }
  if(tmpSys != 0)
    {
      this->addLink(tmpSys, sys);
      this->createVortexRings(tmpSys);
    }
}

int		GameCore::nbOfLinksInSystem(int sys)
{
  int		nb, i;

  for(nb = 0, i = 0; i < (int)vortexList.size(); i++)
    if(vortexList.at(i)->sysId1 == sys
       || vortexList.at(i)->sysId2 == sys)
      nb++;
  return(nb);
}

void		GameCore::addLink(int sys1, int sys2)
{
  vortexCoords	*coords;
  int		dist;

  dist = this->getDist(sys1, sys2);
  coords = new vortexCoords;
  coords->sysId1 = sys1;
  coords->sysId2 = sys2;
  coords->linkType = GAME_CORE_WHITE_LINK;
  if(dist > GAME_CORE_DISTANCE_RED_LINK)
    coords->linkType = GAME_CORE_RED_LINK;
  coords->size = dist;
  this->vortexList.push_back(coords);
}

void		GameCore::createSystems()
{
  int		i;
  int		x, y, z, name;
  SolarSystem	*sys;

  this->createNameList();

  srand(time(NULL));
  for(i = 0; i < this->gameParams->getStarDensity(); i++)
    {
      x = rand() %  GAME_CORE_GALAXY_MAX_SIZE;
      if(rand() % 2 == 0)
	x = -x;
      y = rand() %  GAME_CORE_GALAXY_MAX_SIZE;
      if(rand() % 2 == 0)
	y = -y;
      z = rand() %  GAME_CORE_GALAXY_MAX_SIZE;
      if(rand() % 2 == 0)
	z = -z;
      if(coordsAlreadyExist(x, y, z))
	i--;
      else
	{
	  sys = new(SolarSystem);
	  sys->setCoords(x, y, z);
	  sys->setSize(rand() % 3);
	  sys->setColor(rand() % 4);
	  name = rand() % this->namesList.size();
	  sys->setName(this->namesList.at(name));
	  this->namesList.erase(this->namesList.begin() + name);
	  this->systems.push_back(sys);
	}
    }
}

void		GameCore::createNameList()
{
  QDomDocument doc( "PlanetList" );
  QFile file( "../xml/planet_names.xml" );
  file.open(QIODevice::ReadOnly);
  doc.setContent(&file);
  file.close();

  QDomElement root = doc.documentElement();
  QDomNode n = root.firstChild();
  this->namesList.clear();
  while(!n.isNull())
    {
      QDomElement e = n.toElement();
      if(!e.isNull())
	{
	  if(e.tagName() == "name")
	    this->namesList.push_back(e.attribute( "label", "" ).toStdString());
	}
      n = n.nextSibling();
    }
}

bool		GameCore::coordsAlreadyExist(int x, int y, int z)
{
  int		i;
  SolarSystem	*sys;


  for(i = 0; i < (int)this->systems.size(); i++)
    {
      sys = this->systems.at(i);
      if(sys->getX() / 10 == x / 10
	 && sys->getY() / 10 == y / 10
	 && sys->getZ() / 10 == z / 10)
	return true;
    }
  return false;
}

void		GameCore::cleanData()
{
  int		i;

  for(i = 0; i < (int)this->systems.size(); i++)
    {
      //delete this->systems.at(i);
    }
}

std::vector<SolarSystem*> *GameCore::getSolarSystems()
{
  return(&this->systems);
}

void		GameCore::createPlanets()
{
  int		i, j;
  int		nbOfplnt, x, y, z;
  Planet	*planet;
  std::string	name;

  for(i = 0; i < (int)this->systems.size(); i++)
    {
      for(j = 0, nbOfplnt = rand() % GAME_CORE_MAX_PLANETS_IN_SYSTEM + 1
	    ; j < nbOfplnt; j++)
	{
	  x = rand() %  GAME_CORE_GALAXY_MAX_SIZE;
	  if(rand() % 2 == 0)
	    x = -x;
	  y = rand() %  GAME_CORE_GALAXY_MAX_SIZE;
	  if(rand() % 2 == 0)
	    y = -y;
	  z = rand() %  GAME_CORE_GALAXY_MAX_SIZE;
	  if(rand() % 2 == 0)
	    z = -z;
	  if(!this->checkPlanetCoordsNotUsed(i, x, y, z))
	    j--;
	  else
	    {
	      planet = new(Planet);
	      name.clear();
	      name = this->systems.at(i)->getName();
	      planet->setName(this->systems.at(i)->getName() += " I");
	      switch(this->systems.at(i)->getPlanetListSize())
		{
		case 0:
		  planet->setName(name += " I");
		  break;
		case 1:
		  planet->setName(name += " II");
		  break;
		case 2:
		  planet->setName(name += " III");
		  break;
		case 3:
		  planet->setName(name += " IV");
		  break;
		case 4:
		  planet->setName(name += " V");
		  break;
		case 5:
		  planet->setName(name += " VI");
		  break;
		case 6:
		  planet->setName(name += " VII");
		  break;
		case 7:
		  planet->setName(name += " VIII");
		  break;
		case 8:
		  planet->setName(name += " IX");
		  break;
		case 9:
		  planet->setName(name += " X");
		  break;
		}
	      planet->setSize(rand() % PLANET_SIZE_SIZE + 1);
	      planet->setType(rand() % PLANET_TYPE_SIZE + 1);
	      planet->setSolSysId(i);
	      this->planetList.push_back(planet);
	      this->systems.at(i)->planetList.push_back(planet);
	    }
	}
    }
}

bool		GameCore::checkPlanetCoordsNotUsed(int sysId, int x, int y, int z)
{
  int		i;

  if((x / 50) + (y /50) + (z /50) == 0)
    return false;
  for(i = 0; i < (int)this->systems.at(sysId)->getPlanetListSize(); i++)
    {
      if(x / 25 == this->systems.at(sysId)->planetList.at(i)->getX() / 25
	 && y / 25 == this->systems.at(sysId)->planetList.at(i)->getY() / 25
	 && z / 25 ==this->systems.at(sysId)->planetList.at(i)->getZ() / 25)
	{
	  return false;
	}
    }
  return true;
}
